﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////


#include "CSVFileManager.h"
#include "StringUtil.h"
#include "FilePath.h"
#include "GbmpFileSystem.h"
#include "FamilyDatabaseUtil.h"
#include <QTextStream>
#include <QApplication>
#include <QDir>
#include "IFamilyManager.h"
#include "ExprEngineIntf.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"

using namespace gcmp;
using namespace gfam;
CSVFileManager::CSVFileManager(gcmp::IDocument* pDoc):m_pDoc(pDoc)
{
    if (!pDoc)
    {
        return;
    }
    m_db = gfam::FamilyDatabaseUtil::CreateFamilyDatabase(pDoc);
}
CSVFileManager::~CSVFileManager()
{
}
bool CSVFileManager::IsCSVFileExist(const std::wstring & tableName) const
{
    DBG_WARN_AND_RETURN_UNLESS(m_db, false, L"m_db为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTableList> tables = m_db->GetTables();
    DBG_WARN_AND_RETURN_UNLESS(tables, false, L"m_db为空", L"paramodel", L"2024-02-20");
    if (tables->GetCount() <= 0)
    {
        return false;
    }
    for (int i = 0; i < tables->GetCount(); i++)
    {
        std::shared_ptr<IFamilyDataTable> table = tables->GetItem(i);
        if (table->GetTableName() == tableName)
        {
            return true;
        }
    }
    return false;
}

bool CSVFileManager::DeleteCSVFile(const std::wstring & fileName, std::wstring & error)
{
    if (fileName.empty())
    {
        return false;
    }
    DBG_WARN_AND_RETURN_UNLESS(m_db, false, L"m_db为空", L"paramodel", L"2024-02-20");
    bool isUsed =IsCSVFileUsed(fileName);
    if (isUsed)
    {
        error = L"此查找表格已被表达式引用，因此无法删除。";
        return false;
    }
    m_db->RemoveTable(fileName);
    return true;
}

bool CSVFileManager::ImportCSVFile(const std::wstring& fileName, const std::wstring& filePath, std::wstring & error)
{
    bool result = false;
    std::vector<std::pair<std::wstring, gfam::FamilyDataType>> columnList;
    std::vector<std::vector<std::wstring>> values;
    result = ParseCSVFile(fileName, filePath, columnList, values);
    if (result&&columnList.size() > 0 && values.size() > 0)
    {
        //检查列名是否符合规范
        for (int i=0; i < columnList.size();i++)
        {
            std::pair<std::wstring, gfam::FamilyDataType> item = columnList[i];
            std::wstring columnName = item.first;
            std::vector<std::wstring> nameList = gcmp::StringUtil::SplitString(columnName, L"##");
            if (nameList .size()!=2)
            {
                error = gcmp::StringUtil::FormatWString(L"%s第%d列列名不符合规范，请修改后重试。",fileName.c_str(),i+1);
                return false;
            }
            if (nameList[1].empty())
            {
                error = gcmp::StringUtil::FormatWString(L"%s第%d列列名缺少类型，请修改后重试。", fileName.c_str(), i + 1);
                return false;
            }
            if (item.second==gfam::FamilyDataType::Null)
            {
                error = gcmp::StringUtil::FormatWString(L"%s第%d列类型错误，请修改后重试。", fileName.c_str(), i + 1);
                return false;
            }
        }
        //
        DBG_WARN_AND_RETURN_UNLESS(m_db, false, L"m_db为空", L"paramodel", L"2024-02-20");
        std::shared_ptr<IFamilyDataTable> newTable = m_db->NewTable();
        DBG_WARN_AND_RETURN_UNLESS(newTable, false, L"newTable为空", L"paramodel", L"2024-02-20");
        newTable->SetTableName(fileName);
        for (int i = 0; i < columnList.size(); i++)
        {
            std::pair<std::wstring, gfam::FamilyDataType> item = columnList[i];
            std::shared_ptr<IFamilyDataColumn> newColumn = newTable->NewColumn();
            DBG_WARN_AND_RETURN_UNLESS(newColumn, false, L"newColumn为空", L"paramodel", L"2024-02-20");
            newColumn->SetColumnName(item.first);
            newColumn->SetDataType(item.second);
            newTable->AddColumn(newColumn);
        }

        for (int j = 0; j < values.size(); j++)
        {
            std::vector<std::wstring> rowValueList = values[j];
            if (rowValueList.size()!=columnList.size())
            {
                error = gcmp::StringUtil::FormatWString(L"%s第%d行数据个数和列数不匹配，请修改后重试。", fileName.c_str(), j + 1);
                return false;
            }
            std::shared_ptr<IFamilyDataRow> newRow = newTable->NewRow();
            for (int k = 0; k < rowValueList.size(); k++)
            {
                std::wstring valueStr = rowValueList[k];
                if (valueStr.empty())
                {
                    error = gcmp::StringUtil::FormatWString(L"%s第%d行第%d列数据错误，请修改后重试。", fileName.c_str(),j+1 ,k + 1);
                    return false;
                }
                gfam::FamilyDataType datetype = columnList[k].second;
                bool isOk = true;
                switch (datetype)
                {
                case gfam::FamilyDataType::Int32:
                {
                    Int32 value = StringUtil::ToNumber<Int32>(valueStr,&isOk);
                    newRow->SetAsInt(k, value);
                }
                break;
                case gfam::FamilyDataType::Int64:
                {
                    Int32 value = StringUtil::ToNumber<Int32>(valueStr, &isOk);
                    newRow->SetAsInt(k, value);
                }
                break;
                case gfam::FamilyDataType::Double:
                {
                    double value = StringUtil::ToNumber<double>(valueStr, &isOk);
                    newRow->SetAsDouble(k, value);
                }
                break;
                case gfam::FamilyDataType::String:
                    newRow->SetAsString(k, valueStr);
                    break;
                default:
                    break;
                }
                if (!isOk)
                {
                    error = gcmp::StringUtil::FormatWString(L"%s第%d行第%d列数据格式错误，请修改后重试。", fileName.c_str(), j + 1, k + 1);
                    return false;
                }

            }
            newTable->AddRow(newRow);
        }
        m_db->AddTable(newTable);
        return true;
    }
    else
    {
        error = L"解析CSV文件内容失败，请检查文件内容是否规范。";
        return false;
    }
    return false;
}

bool CSVFileManager::ExportCSVFile(const std::wstring & fileName, const std::wstring & filePath, std::wstring & error)
{
    bool result = false;
    std::vector<std::pair<std::wstring, gfam::FamilyDataType>> columnList;
    std::vector<std::vector<std::wstring>> values;
    std::shared_ptr<gfam::IFamilyDataTable> table = GetTableByName(fileName);
    if (!table)
    {
        return false;
    }
    std::shared_ptr<IFamilyDataColumnList> columns = table->GetColumns();
    for (int i = 0; i < columns->GetCount(); i++)
    {
        std::shared_ptr<IFamilyDataColumn> column = columns->GetItem(i);
        std::wstring columnName = column->GetColumnName();
        gfam::FamilyDataType dataType = column->GetDataType();
        columnList.emplace_back(std::make_pair(columnName, dataType));
    }
    std::shared_ptr<IFamilyDataRowList> rows = table->GetRows();
    for (int j = 0; j < rows->GetCount(); j++)
    {
        std::vector<std::wstring> valueList;
        std::shared_ptr<IFamilyDataRow> row = rows->GetItem(j);
        for (int k = 0; k < row->GetCount(); k++)
        {
            gfam::FamilyDataType datetype = columnList[k].second;
            std::wstring valueStr;
            switch (datetype)
            {
            case gfam::FamilyDataType::Int32:
            {
                int value = row->GetAsInt(k);
                valueStr = std::to_wstring(value);
            }
            break;
            case gfam::FamilyDataType::Int64:
            {
                int value = row->GetAsInt(k);
                valueStr = std::to_wstring(value);
            }
            break;
            case gfam::FamilyDataType::Double:
            {
                double value = row->GetAsDouble(k);
                valueStr = std::to_wstring(value);
            }
            break;
            case gfam::FamilyDataType::String:
                valueStr = row->GetAsString(k);
                break;
            default:
                break;
            }
            valueList.emplace_back(valueStr);
        }
        values.emplace_back(valueList);
    }
    if (columnList.size() > 0 && values.size() > 0)
    {
        result = WriteCSVFile(fileName, filePath, columnList, values);
        if (!result)
        {
            error = L"写CSV文件失败";
        }
    }
    return result;
}

bool gfam::CSVFileManager::LoadDefaultCSVFile(const std::wstring& defaultFileDir, std::wstring& error)
{
    QDir dir(QString::fromStdWString(defaultFileDir));
    if (!dir.exists())
    {
        return true;
    }
    QStringList filters;
    filters << "*.csv";
    QFileInfoList fileList = dir.entryInfoList(filters,QDir::Filter::Files);
    for (int i = 0; i < fileList.size(); i++)
    {
        QFileInfo file = fileList[i];
        QString fileName = file.completeBaseName();
        QString filePath = file.filePath();
        if (IsCSVFileExist(fileName.toStdWString()))
        {
            continue;
        }
        if (!ImportCSVFile(fileName.toStdWString(), filePath.toStdWString(), error))
        {
            return false;
        }
    }
    return true;
}

bool CSVFileManager::ParseCSVFile(const std::wstring & name, const std::wstring & filePath, std::vector<std::pair<std::wstring, gfam::FamilyDataType>>& columnList, std::vector<std::vector<std::wstring>>& values)
{
    QFile file(QString::fromStdWString(filePath));

    if (!file.open(QIODevice::ReadOnly))
    {
        return false;
    }
    QTextStream fileStream;
    fileStream.setDevice(&file);
    //列
    QString line = fileStream.readLine();
    QStringList list =line.split(',');
    for (int i = 0; i < list.size(); i++)
    {
        QString  one =list[i];
        std::wstring item = one.toStdWString();
        if (item.empty())
        {
            item = gcmp::StringUtil::FormatWString(L"default%d##文字",i);
        }
        //第一列特殊处理为默认列
        if (i==0)
        {
            item = L"默认列##文字";
        }
        gfam::FamilyDataType type = GetTypeByName(item, L"##");
        columnList.emplace_back(make_pair(item, type));
    }
    //值
    while (!fileStream.atEnd())
    {
        line = fileStream.readLine();
        list = line.split(',');
        std::vector<std::wstring> value;
        for (auto it = list.begin(); it != list.end(); it++)
        {
            value.emplace_back(it->toStdWString());
        }
        values.emplace_back(value);

    }
    file.close();
    return true;
}
bool CSVFileManager::WriteCSVFile(const std::wstring & name, const std::wstring & filePath, std::vector<std::pair<std::wstring, gfam::FamilyDataType>>& columnList, std::vector<std::vector<std::wstring>>& values)
{
    QFile file(QString::fromStdWString(filePath));
    if (!file.open(QIODevice::WriteOnly))
    {
        return false;
    }
    QTextStream fileStream(&file);
    std::wstring line;
    //列
    for (int i = 0; i < columnList.size(); i++)
    {
        std::pair<std::wstring, gfam::FamilyDataType> column = columnList[i];
        std::wstring name = column.first;
        if (name.substr(0,7)==L"default")
        {
            name = L"";
        }
        //第一列特殊处理为默认列
        if (i==0)
        {
            name = L"默认列";
        }
        line += name + L",";
    }
    line = line.substr(0, line.size() - 1);
    fileStream << QString::fromStdWString(line) << "\r\n";
    //值
    for (int j = 0; j < values.size(); j++)
    {
        std::wstring line;
        std::vector<std::wstring> valueList = values[j];
        for (int k = 0; k < valueList.size(); k++)
        {
            std::wstring value = valueList[k];
            line += value + L",";
        }
        line = line.substr(0, line.size() - 1);
        fileStream << QString::fromStdWString(line) << "\r\n";
    }
    fileStream.flush();
    file.close();
    return true;
}
gfam::FamilyDataType CSVFileManager::GetTypeByName(std::wstring& name, const std::wstring& seperator)
{
    //sqlite 字段默认类型
    gfam::FamilyDataType type = gfam::FamilyDataType::Null;
    if (name.empty())
    {
        return type;
    }
    std::vector<std::wstring> list = gcmp::StringUtil::SplitString(name, seperator);
    if (list.size() > 1)
    {
        std::wstring typeStr = list[1];
        if (typeStr == L"长度")
        {
            type = gfam::FamilyDataType::Double;
        }
        else if (typeStr == L"角度")
        {
            type = gfam::FamilyDataType::Double;
        }
        else if (typeStr == L"文字")
        {
            type = gfam::FamilyDataType::String;
        }
        else if (typeStr == L"整数")
        {
            type = gfam::FamilyDataType::Int32;
        }
        else if (typeStr == L"数值")
        {
            type = gfam::FamilyDataType::Double;
        }
    }
    return type;
}

std::shared_ptr<gfam::IFamilyDataTable> CSVFileManager::GetTableByName(const std::wstring & tableName)
{
    DBG_WARN_AND_RETURN_UNLESS(m_db, nullptr, L"m_db为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTableList> tables = m_db->GetTables();
    DBG_WARN_AND_RETURN_UNLESS(tables, nullptr, L"tables为空", L"paramodel", L"2024-02-20");
    for (int i = 0; i < tables->GetCount(); i++)
    {
        std::shared_ptr<IFamilyDataTable> table = tables->GetItem(i);
        if (table->GetTableName() == tableName)
        {
            return table;
        }
    }
    return nullptr;
}

bool gfam::CSVFileManager::IsCSVFileUsed(const std::wstring & fileName)
{
    bool result = false;
    if (fileName.empty())
    {
        return false;
    }
    IFamilyManager *pFamilyManager = gfam::IFamilyManager::Get(m_pDoc);
    DBG_WARN_AND_RETURN_FALSE_UNLESS(pFamilyManager, L"pFamManager为空", L"paramodel", L"2024-02-20");
    std::vector<ElementId> paramDefineIds = pFamilyManager->GetFamilyParameterDefinitionIds();
    OwnerPtr<IExprEngine> opEngine = CreateEngine();
    if (opEngine==nullptr)
    {
        return false;
    }
    for (int i = 0; i < (int)paramDefineIds.size(); i++)
    {
        const IFamilyParameterDefinition* pParam = pFamilyManager->GetFamilyParameterDefinition(paramDefineIds[i]);
        if ((pParam!=nullptr) && pParam->IsDeterminedByFormula() && !pParam->GetFormula().empty() && (pParam->GetFormula().find(L"size_lookup") != -1))
        {
            IExprParser* Parser = opEngine->FindParser(pParam->GetName().c_str());
            if (Parser == nullptr)
                continue;

            if (Parser->IsCircularReferenced(pParam->GetName().c_str()))
            {
                return true;
            }
            Parser->Evaluate();
            if (Parser->GetIsValid())//如果表达式合法
            {
                const ExprNode*  rootNode = Parser->GetRootNode();
                ExprNode * node = const_cast<ExprNode*>(rootNode);
                if (CheckExprNode(node, fileName))
                {
                    return true;
                }
            }
        }
    }
    return result;
}

bool gfam::CSVFileManager::CheckExprNode(ExprNode * exprNode, const std::wstring & fileName)
{
    bool result = false;
    ExprCustumNode* customNode = dynamic_cast<ExprCustumNode*>(exprNode);
    if (customNode == nullptr)
    {
        return false;
    }
    ExprNode* leftNode = customNode->LeftChild.get();
    ExprNode* rightNode = customNode->RightChild.get();
    bool leftRet = false;
    bool rightRet = false;
    //叶子节点
    if ((!leftNode) && (!rightNode))
    {
        ExprFunctionNode* funcnode = dynamic_cast<ExprFunctionNode*>(exprNode);
        if (funcnode)
        {
            CustomFunctionParams* params = funcnode->GetParamsFW();
            if ((params != nullptr) && (params->Count() > 0))
            {
                ExprNodeData* item = params->GetParam(0);
                std::wstring tablename = L"";
                if (item != nullptr)
                {
                    tablename = item->GetAsString();
                }
                if (tablename == fileName)
                {
                    return true;
                }
            }
        }
    }
    //左子树
    if (leftNode != nullptr)
    {
        leftRet = CheckExprNode(leftNode, fileName);
    }
    //右子树
    if (rightNode != nullptr)
    {
        rightRet = CheckExprNode(rightNode, fileName);
    }
    result = rightRet || leftRet;
    return result;
}

int CSVFileManager::GetFileCount() const
{
    DBG_WARN_AND_RETURN_UNLESS(m_db, -1, L"m_db为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTableList> tables = m_db->GetTables();
    DBG_WARN_AND_RETURN_UNLESS(tables, -1, L"tables为空", L"paramodel", L"2024-02-20");
    return tables->GetCount();
}

std::wstring CSVFileManager::GetFileNameByIndex(int index) const
{
    DBG_WARN_AND_RETURN_UNLESS(m_db, L"", L"m_db为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTableList> tables = m_db->GetTables();
    DBG_WARN_AND_RETURN_UNLESS(tables, nullptr, L"tables为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTable> table = tables->GetItem(index);
    DBG_WARN_AND_RETURN_UNLESS(table, nullptr, L"table为空", L"paramodel", L"2024-02-20");
    std::wstring name = table->GetTableName();
    return name;
}

int gfam::CSVFileManager::GetFileIndexByName(const std::wstring name) const
{
    DBG_WARN_AND_RETURN_UNLESS(m_db, -1, L"m_db为空", L"paramodel", L"2024-02-20");
    std::shared_ptr<IFamilyDataTableList> tables = m_db->GetTables();
    DBG_WARN_AND_RETURN_UNLESS(tables, -1, L"tables为空", L"paramodel", L"2024-02-20");
    for (int i = 0; i < tables->GetCount(); i++)
    {
        std::shared_ptr<IFamilyDataTable> table = tables->GetItem(i);
        DBG_WARN_AND_RETURN_UNLESS(table, -1, L"table为空", L"paramodel", L"2024-02-20");
        if (table->GetTableName() == name)
        {
            return i;
        }
    }
    return -1;
}
